iT邦幫忙

2021 iThome 鐵人賽

DAY 21
0
Modern Web

初學者跪著學JavaScript系列 第 21

初學者跪著學JavaScript Day21 : 原型畢露(下)

  • 分享至 

  • xImage
  •  

一日客語:中文:圓 客語: 眼ienˇ

學習內容

  • 檢查實例的建構器類型:instanceof、constructor
  • 利用實例的constructor屬性來創建物件
  • 解惑昨天問題
  • 透過原型可以做到複製

前述

昨天講述為啥修改了Cat原型後使用Object.getPrototypeOf(niki).constructor
為何是印出 Object 呢
一開始 Object.getPrototypeOf(nini).constructor
明明是Cat為何原型有這種差異呢

就繼續看下去~


function Cat() {
    this.eat = () => 'apple';
}

const nini = new Cat();
Cat.prototype.sleep = 'sleep';
console.log(Object.getPrototypeOf(nini)); //{ sleep: 'sleep' }

//原型變動
//新物件改掉Cat原型
Cat.prototype = {
    go: function () {
        return 'go';
    },
};

const niki = new Cat();

console.log(Object.getPrototypeOf(niki)); //{ go: [Function: go] }
Object.getPrototypeOf(niki).constructor//Object

檢查實例的建構器類型:instanceof、constructor

instanceof :niki instanceof Cat 判斷實例是不是透過建構器函式建立的
constructor:實例.constructor 可以找到創建實例的建構函式

instanceof

wendy instanceof Wendy

運作方式:
會先查Wendy的原型

此原型是否出現在wendy的原型鏈上

function Wendy() {}
function Ann() {}
Ann.prototype = new Wendy(); //目前 Ann.prototype ={}
const smallAnn = new Ann();

console.log(smallAnn instanceof Ann);//true
//Ann的原型是否出現在smallAnn上

console.log(smallAnn instanceof Wendy);//true
//Wendy的原型是否出現在smallAnn上

實例的屬性:constructor

實例.constructor:實例的原始函式參照(物件的起源)

要注意一件事情:
let a = new Apple()
什麼property都沒有只剩下[[prototype]]

點開[[prototype]],從這區開始(記得這裡是物件喔)會是Apple.prototype

此時就要想想,prototype是建構函式才會有,就要思考來自哪個函式就會是他的constructor

所以a.constructor是來自Apple的prototype
這個prototype是因為Apple function 才有的,所以constructor是Apple

(忍耐一下這邊是需要停下來好好思考的,想個幾次就會海闊天空)


現在在實例來找constructor

function Cat() {
    this.eat = () => 'apple';
}
const nini = new Cat();

console.log(nini.constructor);//[Function: Cat]
function Dog() {
    this.sleep = () => 'sleep';
}
const doggy = new Dog();
console.log(doggy.constructor);//Function: Dog]

兩個實例一個nini和一個doggy 找找他們建構子

可以看到nini.constructor和doggy.constructor的原始函式參照是[Function: Cat][Function: Dog]


利用實例constructor屬性來創建物件

可以說是 nini.constructor等同於Cat

因此可以使用 nini.constructor來創建新物件

function Cat() {
    this.eat = () => 'apple';
}
const nini = new Cat();
const kiki = new nini.constructor();//我直接找Cat function的參照來建立實例
console.log('nini使用建構函式創出', nini);

//nini使用建構函式創出 Cat { eat: [Function (anonymous)] }
console.log('kiki使用nini的建構子創出', kiki);

//kiki使用nini的建構子創出 Cat { eat: [Function (anonymous)] }
console.log(nini.constructor == kiki.constructor);//true
console.log(nini instanceof Cat);//true

nini.constructorkiki.constructor都是同樣的指向Cat

也可以試試看基本型別

let name = 'wendy';
let mystring = name.constructor('abc');
console.log(mystring);//'abc'

name由String()產生的實例所以他的mystring.__proto__會是String的prototype
String的prototype內屬性:[[prototype]]會是Object的prtotype
Object的prtotype內屬性[[prototype]]會是null


會了這些,繼續講昨天的疑問

function Cat() {
    this.eat = () => 'apple';
}
const nini = new Cat();
Cat.prototype.sleep = 'sleep';
console.log(Object.getPrototypeOf(nini)); 
//{ sleep: 'sleep' }
//改變Cat的原型
Cat.prototype = {
    go: function () {
        return 'go';
    },
};

const niki = new Cat();
console.log(nini instanceof Cat); //Cat原型是否出現在nini上:false
console.log(nini.constructor); //[Function: Cat]

// 那新實例(niki)呢?
console.log(niki instanceof Cat);///Cat原型是否出現在niki上:true
console.log(niki.constructor); //[Function: Object]

問題:為何niki.constructor 印出是[Function: Object]
因為

//這是Cat修改後的原型(沒有constructor屬性)
{ go: function () {
        return 'go';
    }, }

這個object沒有constructor屬性,通常建構函式的prototype內有constructor屬性,因為修改了建構函式的prototype所以constuctor消失了!!!

所以跑的這句niki.constructor,其實niki會往上一層找到Cat的prototype

但自建object 沒有constructor,所以會根據原型鏈再往上找,

找到{ }本身的[[prototype]]裡的constructor

會像是長這樣

此時這區是Object地盤(是大寫O),他是建構函式的其中一種,因此他的prototype裡會有constructor,
Object.prototype.constructor 登愣!!!

所以niki才會印出Object


透過原型可以做到複製

1.複製別人的方法/屬性
把constructor的prototype的某一個methods複製過來,
像是底下把Wendy.prototype.sleep methods貼到另一個建構函式的methods內

function Wendy() {}
Wendy.prototype.sleep = '今天睡滿24hr,超爽的';
Wendy.prototype.eat = '今天吃滿24hr';

function Ann() {}
Ann.prototype = { goSleep: Wendy.prototype.sleep }; //wendy 的sleep能力掛在ann身上
const a = new Ann();
console.log(a.goSleep);//今天睡滿24hr,超爽的
console.log(a.eat);//undefined

console.log(a instanceof Ann); //true
console.log(a.constructor); //[Function: Object]

這只是複製不是繼承

console.log(a instanceof Wendy); //false

那要說說繼承了~~

使用原型來實現繼承

function Go() {}
function Run() {}
Go.prototype.school = 'sleep';
Run.prototype = new Go();
const student = new Run();
console.log(student instanceof Go);//true
console.log(student instanceof Run);//true
console.log(student.school);//sleep

Go的原型換成new Go()物件實例

透過student物件來存取school方法,JS執行環境會先對student物件找尋有沒有school方法,沒有方法就回去找他的原型:Go物件

先到這裡~明天再接再厲


上一篇
初學者跪著學JavaScript Day20 : 原型畢露(中)
下一篇
初學者跪著學JavaScript Day22 : 我要原型繼承,constructor又不走丟
系列文
初學者跪著學JavaScript30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言